package laboratoryQ.experiment.settings;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import laboratoryQ.BaseUtils;
import laboratoryQ.experiment.settings.SettingsAnnotations.Hidden;
import laboratoryQ.experiment.settings.SettingsAnnotations.IgnoreWhenCheck;

public class BaseValidateAble implements IValidateable {
	@Hidden
	@IgnoreWhenCheck
	private Map<Field, Object> initVals = new HashMap<Field, Object>();

	@Override
	public void initialise() {
		try {
			InitialiseFields();
		} catch (IllegalArgumentException e) {
			throw new RuntimeException("Unexpected.", e);
		} catch (IllegalAccessException e) {
			throw new RuntimeException("Unexpected.", e);
		}
	}

	@Override
	public void validate()  {
		try {
			unsafeValidate();
		} catch (IllegalArgumentException e) {
			throw new RuntimeException(e);
		} catch (IllegalAccessException e) {
			throw new RuntimeException(e);
		}
	}
	
	private void unsafeValidate() throws IllegalArgumentException, IllegalAccessException {
		StringBuilder sb = new StringBuilder();
		for (Field f : listCheckedFields()) {
			Object o = f.get(this);
			Object old = initVals.get(f);
			boolean equals;
			if (o == null) {
				equals = old == null;
			} else {
				equals = o.equals(old); 
			}
			if (equals) {
				sb.append("\n\t").append(f.getName()).append(";");
			}
		}
		if (sb.length() > 0) {
			throw new RuntimeException("Fields:" + sb + " were not initialised.");
		}
	}
	
	public void InitialiseFields() throws IllegalArgumentException, IllegalAccessException {
		for (Field f : listCheckedFields()) {
			Object o = f.get(this);
			initVals.put(f, o);
		}
	}
	
	protected Field[] listCheckedFields() {
		List<Field> fields = BaseUtils.listFields(this.getClass());
		for (Iterator<Field> it = fields.iterator(); it.hasNext();) {
			Field f = it.next();
			if (f.getName().equals("initVals")) {
				it.remove();
				continue;
			}
			if (f.getType().equals(boolean.class)) {
				it.remove();
				continue;
			}
			if (f.getAnnotation(IgnoreWhenCheck.class) != null) {
				it.remove();
				continue;
			}
			f.setAccessible(true);
		}
		Field[] result = new Field[fields.size()];
		fields.toArray(result);
		return result;
	}
}
